#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <sys/socket.h>
#include <memory>

#include "InetAddr.hpp"
#include "Log.hpp"


const int gdefaultsockfd = -1;
const int gbacklog = 8;

namespace SocketMoudle
{

    using namespace InetAddrMoudle;
    using namespace LogMoudle;


    //这里需要进行前置声明，表明Socket是类类型，不然在其它地方使用智能指针时将会无法识别
    class Socket;

    using SockPtr = std::shared_ptr<Socket>;

    class Socket
    {
    public:
        virtual ~Socket() = default;
        virtual void SocketOrDie() = 0;
        virtual void SetSocketOpt() = 0;
        virtual bool BindOrDie(int port) = 0;
        virtual bool ListenOrDie() = 0;
        virtual SockPtr Accepter(InetAddr *clinet) = 0;
        virtual void Close() = 0;
        virtual int Recv(std::string *out) = 0;
        virtual int Send(const std::string &in) = 0;
        virtual int Fd() = 0;

        void BuildTcpSocket(int port)
        {
            SocketOrDie();
            SetSocketOpt();
            BindOrDie(port);
            ListenOrDie();
        }
    };

    class TcpSocket : public Socket
    {
    private:
        int _sockfd; // 套接字

    public:
        TcpSocket() : _sockfd(gdefaultsockfd)
        {
        }
        TcpSocket(int sockfd) : _sockfd(sockfd)
        {
        }
        virtual ~TcpSocket() {}

        // 套接字创建
        virtual void SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);

            if (_sockfd < 0)
            {
                LOG(Logevel::ERROR) << "socket create error";
                exit(1);
            }

            LOG(Logevel::INFO) << "socket create success: " << _sockfd;
        }

        virtual void SetSocketOpt() override
        {
            // 保证服务器异常断开后可以立即重启，不会有bind问题
            int opt = 1;
            int n = ::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
            (void)n;
        }

        virtual bool BindOrDie(int port) override
        {
            if (_sockfd == gdefaultsockfd)
                return false;

            InetAddr addr(port);

            int n = ::bind(_sockfd, addr.GetAddr(), addr.AddrLen());
            if (n < 0)
            {
                LOG(Logevel::ERROR) << "bind error";
                exit(2);
            }
            LOG(Logevel::INFO) << "bind success: " << _sockfd;
            return true;
        }

        virtual bool ListenOrDie() override
        {
            if (_sockfd == gdefaultsockfd)
                return false;

            int n = ::listen(_sockfd,gbacklog);
            if (n < 0)
            {
                LOG(Logevel::ERROR) << "listen error";
                exit(0);
            }
            LOG(Logevel::DEBUG) << "listen create success: " << _sockfd;
            return true;
        }

        virtual SockPtr Accepter(InetAddr *clinet)override
        {
            if(!clinet)
            return nullptr;

            
           sockaddr_in peer;
           socklen_t len=sizeof(peer);

           int newsockfd=::accept(_sockfd,CONV(&peer),&len);
           //接收客户端的ip地址和端口号

           if(newsockfd<0)
           {
            LOG(Logevel::ERROR)<<"accept error";
            return nullptr;
           }

           //将客户端的ip地址和端口号进行复制

           clinet->SetAddr(peer,len);

           //创建新的套接字对象并进行管理

           return std::make_shared<TcpSocket>(newsockfd);

        }


        virtual void Close()override
        {
            if(_sockfd==gdefaultsockfd)
            return;
            ::close(_sockfd);
        }

        virtual int Recv(std::string *out)override
        {
            char buffer[1024*8];
            auto size=::recv(_sockfd,buffer,sizeof(buffer)-1,0);
            if(size>0)
            {
                buffer[size]=0;
                *out=buffer;
            }
            return size;
        }

        virtual int Send(const std::string &in)override
        {
            auto size=::send(_sockfd,in.c_str(),in.size(),0);
            return size;
        }

        virtual int Fd()override
        {
            return _sockfd;
        }
        
    };

}